package Server;

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.ArrayList;
import java.io.*;

import javax.swing.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.newdawn.slick.SlickException;
import org.newdawn.slick.tiled.TiledMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import Server.EaTGServer.Request;
import Server.EaTGServer.Response;
import cherry.orange.ResourceLoader;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;

public class EaTGServer extends JFrame implements WindowListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private JTextArea output;
	private ArrayList<Player> players = new ArrayList<Player>();
	public ArrayList<FieldPlayer> fplayers = new ArrayList<FieldPlayer>();
	public ArrayList<Ammo> ammo = new ArrayList<Ammo>();
	public String mapData = "";
	public String MapFile = "";
	public String servername = "Test Server";
	public boolean wait = true;
	Document serverMap = null;
	String filePath = "";

	private Server server = new Server();
	public int currentPlayer = 1;
	public int currentPlaying = 0;

	public int maxConnection = 10;

	public EaTGServer me;

	public EaTGServer() {
		super("EaTG Server");
		MapSelector mapLoader = new MapSelector();
		mapLoader.setVisible(true);
		while (wait) {
			try {
				Thread.currentThread();
				Thread.sleep(500);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			factory.setValidating(false);
			DocumentBuilder builder;

			builder = factory.newDocumentBuilder();
			builder.setEntityResolver(new EntityResolver() {
				public InputSource resolveEntity(String publicId,
						String systemId) throws SAXException, IOException {
					return new InputSource(
							new ByteArrayInputStream(new byte[0]));
				}
			});

			filePath = getClass().getProtectionDomain().getCodeSource()
					.getLocation().getPath()
					+ "Maps/".replace("%20", " ") + MapFile;
			serverMap = builder.parse(filePath);
		} catch (SAXException e1) {
		} catch (IOException e1) {
		} catch (ParserConfigurationException e2) {
		}
		// set up ServerSocket
		try {
			me = this;
			server.start();
			server.bind(32847);
			Kryo kryo = server.getKryo();
			kryo.register(Request.class);
			kryo.register(Response.class);
			/*
			 * server.addListener(new Listener() { public void
			 * received(Connection connection, Object object) { if (object
			 * instanceof Request) { Request request = (Request) object;
			 * System.out.println("[Server] " + request.text); if
			 * (request.firstConnect) { Response response = null; if
			 * (currentPlaying > maxConnection - 1) { response = new Response(
			 * "Connection refused, because too many players are on.",
			 * Response.COMM_KICK);
			 * display("Connection with refused, because too many players are on."
			 * ); connection.sendTCP(response); connection.close(); } else {
			 * response = new Response( "Successfully connected to " +
			 * servername, Response.COMM_SEND_DATA, currentPlayer, serverMap,
			 * filePath.substring(0, filePath.lastIndexOf("/")));
			 * connection.sendTCP(response); display(request.nickname +
			 * " has joined the game."); players.add(currentPlayer, new Player(
			 * connection, me, currentPlayer, request.nickname));
			 * players.get(currentPlayer).start(); currentPlayer++; } } } } });
			 */
			server.addListener(new Listener() {
				public void received(Connection connection, Object object) {
					if (object instanceof Request) {
						Request request = (Request) object;
						System.out.println(request.text);

						Response response = new Response();
						response.text = "Thanks!";
						connection.sendTCP(response);
					}
				}
			});
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}

		output = new JTextArea();
		getContentPane().add(new JScrollPane(output), BorderLayout.CENTER);
		output.setText("Server awaiting connections\n");
		addWindowListener(this);
		setSize(300, 300);
		setVisible(true);
	}

	public class Request {
		public static final int COMM_DISCONNECT = -1;
		public static final int COMM_WARN = -2;
		public static final int COMM_KICK = -3;
		public static final int COMM_BAN = -4;

		public static final int COMM_RECEIVE_DATA = 0;

		public static final int COMM_CHAT = 1;
		public static final int COMM_PCHAT = 2;
		public static final int COMM_SCHAT = 3;
		public static final int COMM_NOTICE = 4;

		public String text;
		public int comm;
		public int id;
		public Point location;
		public int health;
		public double angle;
		public Ammo ammo;
		public String nickname;
		public boolean firstConnect = false;

	}

	public class Response {
		public static final int COMM_DISCONNECT = -1;
		public static final int COMM_WARN = -2;
		public static final int COMM_KICK = -3;
		public static final int COMM_BAN = -4;

		public static final int COMM_SEND_DATA = 0;

		public static final int COMM_CHAT = 1;
		public static final int COMM_PCHAT = 2;
		public static final int COMM_SCHAT = 3;
		public static final int COMM_NOTICE = 4;

		public String text;
		public int comm;
		public int id;
		public Document mapFile;
		public String tilesLocation;
		public ArrayList<Ammo> ammo;
		public ArrayList<FieldPlayer> players;

		public Response() {

		}

		public Response(String text, int comm) {
			this.text = text;
			this.comm = comm; // ref.substring(0,ref.lastIndexOf("/"))
		}

		public Response(String text, int comm, ArrayList<Ammo> ammo,
				ArrayList<FieldPlayer> players) {
			this.text = text;
			this.comm = comm; // ref.substring(0,ref.lastIndexOf("/"))
			this.ammo = ammo;
			this.players = players;
		}

		public Response(String text, int comm, int id, Document mapFile,
				String tilesLocation) {
			this.text = text;
			this.comm = comm;
			this.id = id;
			this.mapFile = mapFile;
			this.tilesLocation = tilesLocation;
		}
	}

	public String getFileText(String path) {
		File file = new File(path);
		StringBuffer contents = new StringBuffer();
		BufferedReader reader = null;

		try {
			reader = new BufferedReader(new FileReader(file));
			String text = null;
			while ((text = reader.readLine()) != null) {
				contents.append(text).append(
						System.getProperty("line.separator"));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (reader != null) {
					reader.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return contents.toString();
	}

	public class MapSelector extends javax.swing.JFrame {

		/** Creates new form MapSelector */
		public MapSelector() {
			initComponents();
		}

		/**
		 * This method is called from within the constructor to initialize the
		 * form. WARNING: Do NOT modify this code. The content of this method is
		 * always regenerated by the Form Editor.
		 */
		@SuppressWarnings("unchecked")
		// <editor-fold defaultstate="collapsed" desc="Generated Code">
		private void initComponents() {

			jScrollPane1 = new javax.swing.JScrollPane();
			jList1 = new javax.swing.JList();
			jButton1 = new javax.swing.JButton();
			jButton2 = new javax.swing.JButton();
			setResizable(false);
			setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
			setTitle("Map Selector");

			String MapsDirectory = (getClass().getProtectionDomain()
					.getCodeSource().getLocation().getPath() + "Maps/")
					.replace("%20", " ");
			File folder = new File(MapsDirectory);
			File[] listOfFiles = folder.listFiles();

			final ArrayList<String> files = new ArrayList<String>();
			for (File file : listOfFiles) {
				if (file.getName().toString().endsWith(".tmx")) {
					files.add(file.getName().replace(".tmx", ""));
				}
			}
			jList1.setModel(new javax.swing.AbstractListModel() {

				ArrayList<String> strings = files;

				public int getSize() {
					return strings.size();
				}

				public Object getElementAt(int i) {
					return strings.get(i);
				}
			});
			jScrollPane1.setViewportView(jList1);

			jButton1.setText("Select");
			jButton1.addActionListener(new java.awt.event.ActionListener() {

				public void actionPerformed(java.awt.event.ActionEvent evt) {
					jButton1ActionPerformed(evt);
				}
			});

			jButton2.setText("Cancel");
			jButton2.addActionListener(new java.awt.event.ActionListener() {

				public void actionPerformed(java.awt.event.ActionEvent evt) {
					jButton2ActionPerformed(evt);
				}
			});

			javax.swing.GroupLayout layout = new javax.swing.GroupLayout(
					getContentPane());
			getContentPane().setLayout(layout);
			layout.setHorizontalGroup(layout
					.createParallelGroup(
							javax.swing.GroupLayout.Alignment.LEADING)
					.addGroup(
							layout.createSequentialGroup()
									.addContainerGap()
									.addGroup(
											layout.createParallelGroup(
													javax.swing.GroupLayout.Alignment.TRAILING)
													.addComponent(
															jScrollPane1,
															javax.swing.GroupLayout.Alignment.LEADING,
															javax.swing.GroupLayout.DEFAULT_SIZE,
															234,
															Short.MAX_VALUE)
													.addGroup(
															javax.swing.GroupLayout.Alignment.LEADING,
															layout.createSequentialGroup()
																	.addComponent(
																			jButton1,
																			javax.swing.GroupLayout.PREFERRED_SIZE,
																			114,
																			javax.swing.GroupLayout.PREFERRED_SIZE)
																	.addPreferredGap(
																			javax.swing.LayoutStyle.ComponentPlacement.RELATED)
																	.addComponent(
																			jButton2,
																			javax.swing.GroupLayout.PREFERRED_SIZE,
																			114,
																			javax.swing.GroupLayout.PREFERRED_SIZE)))
									.addContainerGap()));
			layout.setVerticalGroup(layout
					.createParallelGroup(
							javax.swing.GroupLayout.Alignment.LEADING)
					.addGroup(
							javax.swing.GroupLayout.Alignment.TRAILING,
							layout.createSequentialGroup()
									.addContainerGap()
									.addComponent(
											jScrollPane1,
											javax.swing.GroupLayout.PREFERRED_SIZE,
											190,
											javax.swing.GroupLayout.PREFERRED_SIZE)
									.addPreferredGap(
											javax.swing.LayoutStyle.ComponentPlacement.RELATED,
											javax.swing.GroupLayout.DEFAULT_SIZE,
											Short.MAX_VALUE)
									.addGroup(
											layout.createParallelGroup(
													javax.swing.GroupLayout.Alignment.BASELINE)
													.addComponent(jButton1)
													.addComponent(jButton2))
									.addContainerGap()));

			pack();
		}// </editor-fold>

		private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
			MapFile = jList1.getSelectedValue().toString() + ".tmx";
			wait = false;
			dispose();
		}

		private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
			wait = false;
			dispose();
		}

		/**
		 * @param args
		 *            the command line arguments
		 */
		// Variables declaration - do not modify
		private javax.swing.JButton jButton1;
		private javax.swing.JButton jButton2;
		private javax.swing.JList jList1;
		private javax.swing.JScrollPane jScrollPane1;
		// End of variables declaration
	}

	public void display(String s) {
		output.append(s + "\n");
	}

	public boolean gameOver() {
		// Place code here to test for a winner of the game
		return false;
	}

	public static void main(String args[]) {
		EaTGServer game = new EaTGServer();

		game.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
	}

	public FieldPlayer containsPlayID(int id) {
		for (FieldPlayer p : fplayers) {
			if (p.getId() == id) {
				return p;
			}
		}
		return null;
	}

	// "ammo:" + a.getStartLoc().x + "x" + a.getStartLoc().y + ":" +
	// a.getAngle() + ":" + a.getType()

	public void parseAmmo(Ammo aa) {
		ammo.add(aa);
	}

	public void parsePlayer(Point loc, int num, String nick, int health,
			double angle) {
		if (containsPlayID(num) == null) {
			fplayers.add(new FieldPlayer(num, "Player " + num, nick, loc,
					health));
		} else {
			for (FieldPlayer p : fplayers) {
				if (p.getId() == num) {
					p.Set(num, "Player " + num, loc, health, angle);
				}
			}
		}
	}

	@Override
	public void windowActivated(WindowEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowClosed(WindowEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowClosing(WindowEvent arg0) {
		server.close();
	}

	@Override
	public void windowDeactivated(WindowEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowDeiconified(WindowEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowIconified(WindowEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowOpened(WindowEvent arg0) {
		// TODO Auto-generated method stub

	}

	public ArrayList<Ammo> getAmmo() {
		return ammo;
	}

	public ArrayList<FieldPlayer> getFplayers() {
		return fplayers;
	}

	// Player class to manage each Player as a thread
	public class Player extends Thread {
		private Connection connection;
		private EaTGServer control;
		private int number;
		private String nick;
		protected boolean threadSuspended = true;

		public Player(Connection c, EaTGServer t, int num, String nickname) {

			connection = c;

			// Set a timeout of 'timeout_length' milliseconds
			try {
				connection.setTimeout(3000);
			} catch (Exception se) {
				se.printStackTrace();
			}
			control = t;
			number = num;
			nick = nickname;
		}

		/*
		 * public String generatePlayerOutput(int ignoreID) { //
		 * "players;57,LolTest,10:20`58,TesetLol,20:10" String base =
		 * "players;"; int max = control.fplayers.size(); int count = 0; for
		 * (FieldPlayer fp : control.fplayers) { if (fp.getId() == ignoreID)
		 * continue; // TODO check if player is speed hacking and reset //
		 * position base = base + fp.getId() + "," + fp.getName() + "," +
		 * fp.getLoc().x + ":" + fp.getLoc().y + "," + fp.getLatency(); if
		 * (count < max) base = base + "`"; count++; } if (count == 0) return
		 * null; return base; }
		 * 
		 * public String generateAmmoOutput() { //
		 * "players;57,LolTest,10:20`58,TesetLol,20:10" String base = "ammo;";
		 * int max = control.ammo.size(); int count = 0; for (Ammo am :
		 * control.ammo) { // TODO aimbot prevention? idk lol // TODO handle
		 * destroying and generating ammo base = base + "make," + am.getAmmoID()
		 * + "," + am.getStartLoc().x + ":" + am.getStartLoc().y + "," +
		 * am.getAngle() + "," + am.getDistance(); if (count < max) base = base
		 * + "`"; count++; } if (count == 0) return null; return base; }
		 */

		public void removeByID(int id) {
			// "players;57,LolTest,10:20`58,TesetLol,20:10"
			for (int i = 0; i < control.fplayers.size(); i++) {
				if (control.fplayers.get(i).getId() == id) {
					control.fplayers.remove(i);
				}
			}
		}

		public void updatePing(int id, long ping) {
			// "players;57,LolTest,10:20`58,TesetLol,20:10"
			for (int i = 0; i < control.fplayers.size(); i++) {
				if (control.fplayers.get(i).getId() == id) {
					control.fplayers.get(i).setLatency(ping);
				}
			}
		}

		public void run() {

			control.display("Player " + number + " connected");
			// connection.sendTCP(arg0).println(number + ":" + control.mapData);
			control.currentPlaying++;

			// Play game
			connection.addListener(new playerListener(control));
		}

		public class playerListener extends Listener {
			EaTGServer control;

			public playerListener(EaTGServer c) {
				control = c;
			}

			public void received(Connection connection, Object object) {
				if (object instanceof Request) {
					try {
						Request request = (Request) object;
						System.out.println("[Server] " + request.text);
						Response response = null;
						// control.display( "From " + number + ": " + location);
						if (request.location != null) {
							control.parsePlayer(request.location, number, nick,
									request.health, request.angle);
						}
						if (request.ammo != null) {
							control.parseAmmo(request.ammo);
						}
						response = new Response("", Response.COMM_SEND_DATA,
								control.getAmmo(), control.getFplayers());
					} catch (Exception e) {
					}

				}

			}
		}
	}
}